<template>
    <div>
        <canvas
            :style="'border-radius: '+borderRadius+'px;opacity: '+ opacity"
            id="canvas"
            ref="canvas"
            :width="width"
            :height="height"
            @click="updateCanvas">
        </canvas>
    </div>
    <!--
    ## 属性

    value：图形验证码内容（由后端提供时填写）

    nums: 验证码长度(number)，默认：4

    width: 图形验证码宽度(number)

    height: 图形验证码高度(number)

    color: 图形验证码内容颜色(string)

    randomLine：是否开启随机干扰线条(boolean)

    clickUpdate：是否开启点击更新图形验证码(boolean)

    ## 事件

    getCode: 获取图形验证码内容及结果
    -->
</template>

<script>
export default {
    name: "index",
    data() {
        return {
            ctx: null,
            computedWidth: 0,
            computedHeight: 0,
            globalOperationIndex: 0
        }
    },
    props: {
        value: {
            type: String,
            default: "",
        },
        nums: {
            type: Number,
            default: 4,
        },
        borderRadius: {
            type: Number,
            default: 0,
        },
        opacity: {
            type: String,
            default: "1"
        },
        width: {
            type: Number,
            default: 300,
        },
        height: {
            type: Number,
            default: 80,
        },
        color: String,
        randomLine: {
            type: Boolean,
            default: false,
        },
        clickUpdate: {
            type: Boolean,
            default: true,
        },
        beginColor: {
            type: String,
            default: "#000"
        },
        endColor: {
            type: String,
            default: "#fff"
        },
        interfereLine: {
            type: Boolean,
            default: true
        },
        interferePoint: {
            type: Boolean,
            default: true
        },
        linearG: {
            type: Array,
            default: [0, 0, 100, 80]
        }
    },
    mounted() {
        const ramdomStr = this.getRandomStr();
        this.initCanvas();
        this.setCanvas(ramdomStr);
    },
    methods: {
        initCanvas() {
            const canvas = this.$refs.canvas;

            const ctx = canvas.getContext("2d");

            const computedStyle = getComputedStyle(canvas);

            this.ctx = ctx;

            this.computedWidth = parseFloat(computedStyle.width);

            this.computedHeight = parseFloat(computedStyle.height);

            var grd = ctx.createLinearGradient(this.linearG[0], this.linearG[1], this.linearG[2], this.linearG[3]);

            grd.addColorStop(0, this.beginColor);

            grd.addColorStop(1, this.endColor)

            ctx.fillStyle = grd;

            ctx.fillRect(0, 0, this.computedWidth, this.computedHeight);

            if (this.interfereLine) {
                this.setInterfereLine(ctx);
            }

            if (this.interferePoint) {
                this.setInterferePoint(ctx);
            }

            this.randomLine && this.setRandomLine(ctx);

        },

        getRandomStr() {
            /*const _str =
                "abcdefghijkmlnopQrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";*/

            const _strNum = "0123456789";

            const _strNumExZero = "123456789"

            const _strOperation = "+-*";

            let tailLen = Math.floor((this.nums - 1) / 2);

            let headLen = this.nums - 1 - tailLen;

            if (headLen > 1) {
                let tmp = Math.floor(Math.random() * 2)
                if (tmp == 1) {
                    headLen--;
                    tailLen++;
                }
            }

            this.globalOperationIndex = headLen;

            let randomStr = "";

            let head, operation, tail = "";

            if (!this.value) {
                for (var i = 0; i < headLen; i++) {
                    if (headLen > 1 && i == 0) {
                        randomStr += _strNumExZero[Math.floor(Math.random() * _strNumExZero.length)];
                    } else {
                        randomStr += _strNum[Math.floor(Math.random() * _strNum.length)];
                    }
                }
                head = randomStr;
                operation = _strOperation[Math.floor(Math.random() * _strOperation.length)];
                randomStr += operation;
                for (var j = 0; j < tailLen; j++) {
                    if (tailLen > 1 && j == 0) {
                        tail += _strNumExZero[Math.floor(Math.random() * _strNumExZero.length)];
                    } else {
                        tail += _strNum[Math.floor(Math.random() * _strNum.length)];
                    }
                }
                randomStr += tail
            } else {
                randomStr = this.value;
            }

            let res;
            if (operation == "+") {
                res = 1 * head + 1 * tail;
            } else if (operation == "-") {
                res = 1 * head - 1 * tail;
            } else if (operation == "*") {
                res = 1 * head * 1 * tail;
            }

            this.$emit("getCode", randomStr, res);

            return randomStr;
        },

        setInterfereLine(ctx) {
            const computedWidth = this.computedWidth;
            const computedHeight = this.computedHeight;
            for (let i = 0; i <= 5; i++) {
                //验证码上显示干扰线条
                ctx.beginPath();
                ctx.strokeStyle = this.getRandomRgb();
                ctx.moveTo(
                    Math.random() * computedWidth,
                    Math.random() * computedHeight
                );
                ctx.lineTo(
                    Math.random() * computedWidth,
                    Math.random() * computedHeight
                );
                ctx.stroke();
            }
        },

        setInterferePoint(ctx) {
            const computedWidth = this.computedWidth;
            const computedHeight = this.computedHeight;
            for (let i = 0; i <= 30; i++) {
                //验证码上显示小点
                ctx.beginPath();
                ctx.strokeStyle = this.getRandomRgb();
                let x = Math.random() * computedWidth;
                let y = Math.random() * computedHeight;
                ctx.moveTo(x, y);
                ctx.lineTo(x + 1, y + 1);
                ctx.stroke();
            }
        },

        setRandomLine(ctx) {
            const computedWidth = this.computedWidth;
            const computedHeight = this.computedHeight;
            const nums = this.nums;
            const lineNums = Math.floor(computedWidth / nums);

            for (let i = 0; i < lineNums; i++) {
                ctx.save();

                ctx.beginPath();

                ctx.strokeStyle = this.getRandomRgb();

                ctx.translate(
                    10 + i * (computedWidth / lineNums),
                    Math.random() * computedHeight
                );

                ctx.rotate(this.getRandomDeg());

                ctx.moveTo(0, 0);

                ctx.lineTo(0, Math.random() * computedHeight);

                ctx.stroke();

                ctx.restore();
            }

        },

        setCanvas(str) {
            const len = str.length;
            const ctx = this.ctx;
            const fontsize = this.setFont();
            const computedHeight = this.computedHeight;
            const computedWidth = this.computedWidth;

            ctx.font = `${fontsize}px sans-serif`;

            ctx.save();

            for (let i = 0; i < len; i++) {
                const scaleBase = Math.random() > 0.5 ? 1.1 : 0.9;

                const randomRgb = this.getRandomRgb();

                ctx.fillStyle = this.color ?? randomRgb;

                ctx.translate(
                    (computedWidth - fontsize * 4) / len + fontsize * i,
                    parseFloat(computedHeight) / 2
                );
                if(i != this.globalOperationIndex){
                    ctx.rotate(this.getRandomDeg());
                }

                ctx.scale(scaleBase, scaleBase);

                ctx.textBaseline = "middle";

                if(this.nums == 3){
                    ctx.fillText(str[i], 20, 0);
                } else {
                    ctx.fillText(str[i], 0, 0);
                }


                ctx.restore();

                ctx.save();
            }
        },

        updateCanvas() {
            if (!this.clickUpdate) return;

            const ctx = this.ctx;

            const ramdomStr = this.getRandomStr();

            ctx.clearRect(0, 0, this.computedWidth, this.computedHeight);

            this.initCanvas();

            this.setCanvas(ramdomStr);
        },

        getRandomDeg() {
            return (Math.PI / 180) * (-45 + Math.random() * 90);
        },

        getRandomRgb() {
            return `rgb(${Math.floor(Math.random() * 255)},${Math.floor(
                Math.random() * 255
            )},${Math.floor(Math.random() * 255)})`;
        },

        setFont() {
            let fontsize = this.computedWidth / (this.nums * 1.1);

            if (fontsize >= this.computedHeight) {
                fontsize = this.computedHeight * 0.8;
            }

            return fontsize;
        },
    },
}
</script>

<style scoped>

</style>